using System;
using System.Drawing;

namespace OhioState.Graphics
{

    // 
    // OSUColor - float ARGB color representation
    //
    [Serializable()]
    public class OSUColor
	{
        public static readonly OSUColor White = new OSUColor(1.0f, 1.0f, 1.0f, 1.0f);
        public static readonly OSUColor Black = new OSUColor(1.0f, 0.0f, 0.0f, 0.0f);
        public static readonly OSUColor Transparent = new OSUColor(0.0f, 0.0f, 0.0f, 0.0f);
        public static readonly OSUColor Scarlet = new OSUColor(1.0f, 153.0f/255.0f, 0.0f, 0.0f);
        public static readonly OSUColor Grey = new OSUColor(1.0f, 153.0f/255.0f, 153.0f/255.0f, 153.0f/255.0f);
        //
        // Alpha, Red, Green, and Blue values
        //   All values should remain between 0.0 and 1.0
        // I am considering changing this to use an internal
        //   representation of HSV.  It may fix a few problems we have.
        //
		private float a, r, g, b;

        #region Min and Max functions
        //
        // Standard Math Functions needed to calculate HSV
        //  These should probably be put somewhere else and not be a part of this class
        //  - Max of 2 or 3 floats
        //  - Min of 2 or 3 floats
        private float max(float x, float y)
        {
            return (y > x) ? y : x;
        }
        private float max(float x, float y, float z)
        {
            return max(max(x, y), z);
        }
        private float min(float x, float y)
        {
            return (y < x) ? y : x;
        }
        private float min(float x, float y, float z)
        {
            return min(min(x, y), z);
        }
        #endregion


        //
        // Set RGB values using Hue, Saturation, and Value
        // Acceptable input values:
        //  h : 0.0 to 6.0
        //  s and v: 0.0 to 1.0
		public void SetByHSV(float h, float s, float v)
		{
			float m, n, f;
			int i;
	
			if(h == 0)
				r = g = b = v;
			i = (int)h;
			f = h - i;
			if(i % 2 == 0) // if i is even
				f = 1 - f;
			m = v * (1 - s);
			n = v * (1 - s * f);
			switch (i) 
			{
				case 6:
				case 0:
					r = v;
					g = n;
					b = m;
					break;
				case 1: 
					r = n;
					g = v;
					b = m;
					break;
				case 2: 
					r = m;
					g = v;
					b = n;
					break;
				case 3: 
					r = m;
					g = n;
					b = v;
					break;
				case 4: 
					r = n;
					g = m;
					b = v;
					break;
				case 5: 
					r = v;
					g = m;
					b = n;
					break;
			}
		}

        //
		// Constructors
        // User either specifies ARGB, RGB or allows it to default to black
        //
		public OSUColor()
		{
			a = 1.0f;
			r = 0.0f;
			g = 0.0f;
			b = 0.0f;
		}
		public OSUColor(float cR, float cG, float cB)
		{
			a = 1.0f;
			R = cR;
			G = cG;
			B = cB;
		}
		public OSUColor(float cA, float cR, float cG, float cB)
		{
			A = cA;
			R = cR;
			G = cG;
			B = cB;
		}
        public OSUColor(Color c)
		{
			A = (float)c.A / 255.0f;
			R = (float)c.R / 255.0f;
			G = (float)c.G / 255.0f;
			B = (float)c.B / 255.0f;
        }
        public OSUColor(OSUColor c)
        {
            A = c.A;
            R = c.R;
            G = c.G;
            B = c.B;
        }

        #region Public ARGB Properties
        //
        // Public Properties
        // A, R, G, B
        // Error checking is done to ensure valid values are given.
        //
		public float A
		{
			get{ return a; }
			set
			{
				if((value >= 0.0f) && (value <= 1.0f))
					a = value;
			}
		}
		public float R
		{
			get{ return r; }
			set
			{
				if((value >= 0.0f) && (value <= 1.0f))
					r = value;
			}
		}
		public float G
		{
			get{ return g; }
			set
			{
				if((value >= 0.0f) && (value <= 1.0f))
					g = value;
			}
		}
		public float B
		{
			get{ return b; }
			set
			{
				if((value >= 0.0f) && (value <= 1.0f))
					b = value;
			}
		}
        #endregion

		//
        // Public Properties
        // HSV Space colors
        // Be careful of the order that you use these.
        //   Modifying one may have an effect on the others
        //   In general they should be set reverse order, V first, then S, then H
        //
		public float H
		{
			get
			{
				float f, h = 0;
				float fmax = max(r, g, b);
				float fmin = min(r, g, b);
				int i;

				if(S > 0)
				{
					f = (r == fmin) ? g - b : ((g == fmin) ? b - r : r - g);
					i = (r == fmin) ? 3 : ((g == fmin) ? 5 : 1);
					h = i - f/(fmax - fmin);
				}
				if(h == 6)
					h = 0;

				return h;
			}
			set
			{
				float h = value, s = S, v = V;
				SetByHSV(h, s, v);
			}
		}
		public float S
		{
			get
			{
				float fmax = max(r, g, b);
				float fmin = min(r, g, b);
				return (fmax > 0) ? (fmax - fmin)/fmax : 0;
			}
			set
			{
				float h = H, s = value, v = V;
				SetByHSV(h, s, v);
			}
		}
		public float V
		{
			get{ return max(r, g, b); }
			set
			{
				float h = H, s = S, v = value;
				SetByHSV(h, s, v);
			}
        }

        //
        // Public Property to get/set a .NET Color
        //
		public Color Color
		{
			get
			{
				Color temp = Color.FromArgb((int)(a*255), (int)(r*255), (int)(g*255), (int)(b*255));
				return temp;
			}
			set
			{
				A = (float)value.A / 255.0f;
				R = (float)value.R / 255.0f;
				G = (float)value.G / 255.0f;
				B = (float)value.B / 255.0f;
			}
		}
        //
		// Public Property to get the True Color
        //   True color is the RGB color values with 100% opacity
        //   This is needed for coloring Alpha Knots that would normally
        //    show up as transparent if drawn without correction
        //
		public Color TrueColor
		{
			get
			{
				Color temp = Color.FromArgb(255, (int)(r*255), (int)(g*255), (int)(b*255));
				return temp;
			}
			set
			{
				A = 1.0f;
				R = (float)value.R / 255.0f;
				G = (float)value.G / 255.0f;
				B = (float)value.B / 255.0f;
			}
		}
	}
}
